////////////////////////////////////////////////
// DESCRIPTION:
//    Windows CE Power Button Device Driver 
//
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#include <windows.h>
#include <devload.h>
#include <giisr.h>
#include <pm.h>
#include <FusionWare\Win32.h>
#include <FusionWare\DFX.h>
#include <FusionWare\DFX\StreamAPI.h>

using namespace FusionWare::Threads;
using namespace FusionWare::DFX;

// Class to implement the suspend button functionality
// a pointer to an instance of this class is returned
// from the drivers Init() function as the "Driver Context" 
// for the button driver
//
class ButtonHandler
{
public:   
    // Initialize the driver
    DWORD Init(TCHAR* RegPath);
   
    // Uninitialize the driver
    BOOL DeInit(DWORD Context);

protected:
    // IST function for the button
    UINT32 IntHandler(IntSvcThread IntObj);

    bool Suspending;               // flag to indicate if suspending (eliminates "bounce")
    DeviceRegValues RegValues;

    static long RefCount;         // keep track of how many times loaded by device manager 
};

// define the static ref count
long ButtonHandler::RefCount = 0;

// single instance for all; just in case user
// does something stupid and loads the driver
// a second time. A single instance is created
// that ref counts every load/unload and only
// fully de-inits on the last unload.
static ButtonHandler ButtonDev;

///////////////////////////////////////////////////////
// Description: 
// 
// Input: 
//     RegPath - Path to the Active Registry key for this driver
//
// Returns: 
//     Driver context (ButtonHandler instance count in this implementation)
//     
// Remarks: 
//     Only allows one initialization to protect against
//     accidentally creating multiple instances of the driver
// 
DWORD ButtonHandler::Init(TCHAR* RegPath)
{
    if(InterlockedIncrement(&this->RefCount)==1)
    {
        this->Suspending = false;

        // get the device info from the registry
        if(!this->RegValues.ReadRegValues(RegPath, 1))
            return 0;

        // Initialize the IRQ and start the IST.
        this->RegValues.Irqs[0].StartIST( IstHandler(this, &ButtonHandler::IntHandler), 128);
    }

    return this->RefCount;
}

///////////////////////////////////////////////////////
// Description: 
//    De-inits the Driver
//
// Input: 
//    Device Context previously returned from Init()
//
// Remarks: 
//    Checks refernce count and if 0 after decrementing stops 
//    the IST.
//
BOOL ButtonHandler::DeInit(DWORD )
{
    if(0==InterlockedDecrement(&this->RefCount))
        this->RegValues.Irqs[0].StopIST();

    return TRUE;
}


///////////////////////////////////////////////////////
// Description: 
//     Interrupt Handler for the "power" Button
//
// Returns: 
//     Thread exit code - 0
//
// Remarks: 
//     Handles Debounce by processing the press interrupt only once
//     and not reseting the flag until resume has occured.
//
UINT32 ButtonHandler::IntHandler(IntSvcThread IntObj)
{
    while(IntSvcThread::IST_INTERRUPT == IntObj.WaitForInterrupt())
    {
        IntObj.InterruptDone();

        // handle de-bounce by only processing this once
        if(!this->Suspending)
        {
            this->Suspending=true;
        
            // this will not return until resume is complete
            SetSystemPowerState(NULL,POWER_STATE_SUSPEND, POWER_FORCE);
            this->Suspending=false;
        }
    }

    return 0;
}

// Group = Exported Functions
extern "C" {
    
    // Called by device manager at load time.
    // The DLL is set up with order 0 so it is
    // loaded quite early in the boot process.
    DWORD Init(TCHAR* RegPath)
    {
        return ButtonDev.Init(RegPath);
    }

    // Called by device manager at unload time.
    BOOL Deinit(DWORD Context)
    {
        return ButtonDev.DeInit(Context);
    }
}

